//---------------------------------------------------------------------------

#include "vcl_stl.h"
#pragma hdrstop

#include "h_base.h"
#include "h_xml.h"
#include "h_global.h"
using namespace h_base;
using namespace h_xml;

#include "h_g.h"
#include "h_critical.h"

//---------------------------------------------------------------------------

void __fastcall HCritical::InitGraph(set<UTF8String> &ExcludeSet)
{
   CriticalGraph.clear();

   for ( map<UTF8String, HJobListInfo>::const_iterator Iter = Graph.begin();
         Iter != Graph.end(); Iter++ )
   {
      const HJobListInfo &Job = Iter->second;
      if ( Job.Type == "EXTERN_JOB" || Job.Type == "GROUP" )
      {
         continue;
      }
      HCriticalNode Node;
      Node.Name = Job.Name;
      Node.Time = DiffTimeSec(Job.StartTime, Job.EndTime) - Job.IdleTimeSum;
      Node.SuccJobSet = Job.SuccJobSet;
      if ( Job.Type == "EVENT_WAIT" )
      {
         Node.Time = 0;
      }
      if ( ExcludeSet.find(Job.Name) != ExcludeSet.end() )
      {
         Node.Time = 0;
         Node.ExcludeFlag = true;
      }
      CriticalGraph[Node.Name] = Node;
   }

   for ( map<UTF8String, HJobListInfo>::const_iterator Iter = Graph.begin();
         Iter != Graph.end(); Iter++ )
   {
      const HJobListInfo &Job = Iter->second;
      for ( int i = 0; i < Job.ExternJobList.size(); i++ )
      {
         const UTF8String &ExternJob = Job.ExternJobList[i];
         if ( strstr(ExternJob.c_str(), h_g::JobNameDelimiter) )
         {
            AddLink(ExternJob, Job.Name);
         }
         else
         {
            for ( map<UTF8String, HJobListInfo>::iterator Iter2 = Graph.begin();
                  Iter2 != Graph.end(); Iter2++ )
            {
               HJobListInfo &Job2 = Iter2->second;
               if ( Job2.GroupId == ExternJob && Job2.Type != "EXTERN_JOB" && Job2.SuccJobSet.empty() )
               {
                  AddLink(Job2.Name, Job.Name);
               }
            }
         }
      }
      for ( set<UTF8String>::const_iterator Iter = Job.SuccJobSet.begin();
            Iter != Job.SuccJobSet.end(); Iter++ )
      {
         AddLink(Job.Name, *Iter);
      }
   }

   HCriticalNode Start;
   Start.Name = "START";

   for ( map<UTF8String, HCriticalNode>::iterator Iter = CriticalGraph.begin();
         Iter != CriticalGraph.end(); Iter++ )
   {
      Start.SuccJobSet.insert(Iter->first);
   }

   CriticalGraph[Start.Name] = Start;

   for ( map<UTF8String, HCriticalNode>::iterator Iter = CriticalGraph.begin();
         Iter != CriticalGraph.end(); Iter++ )
   {
      HCriticalNode &Critical = Iter->second;
      Critical.SuccJobVec.insert(Critical.SuccJobVec.end(),
         Critical.SuccJobSet.begin(), Critical.SuccJobSet.end());
      if ( Critical.SuccJobIdx == Critical.SuccJobVec.size() )
      {
         Critical.CompletedFlag = true;
         Critical.MaxTime = Critical.Time;
      }
   }
}

void __fastcall HCritical::AddLink(const UTF8String &From, const UTF8String &To)
{
   HJobListInfo &JobFrom = Graph[From];
   HJobListInfo &JobTo = Graph[To];

   if ( JobFrom.Type == "EXTERN_JOB" )
   {
      return AddLink(JobFrom.ExternJob, To);
   }

   if ( JobFrom.Type == "GROUP" )
   {
      for ( map<UTF8String, HJobListInfo>::iterator Iter = Graph.begin();
            Iter != Graph.end(); Iter++ )
      {
         HJobListInfo &Job = Iter->second;
         if ( Job.GroupId == JobFrom.Group && Job.Type != "EXTERN_JOB" &&
              Job.SuccJobSet.empty() )
         {
            AddLink(Job.Name, To);
         }
      }
      return;
   }

   if ( JobTo.Type == "GROUP" )
   {
      for ( map<UTF8String, HJobListInfo>::iterator Iter = Graph.begin();
            Iter != Graph.end(); Iter++ )
      {
         HJobListInfo &Job = Iter->second;
         if ( Job.GroupId == JobTo.Group && Job.Type != "EXTERN_JOB" &&
              Job.InDegreeWithinGroup == 0 )
         {
            AddLink(From, Job.Name);
         }
      }
      return;
   }

   if ( CriticalGraph.find(From) == CriticalGraph.end() ||
        CriticalGraph.find(To) == CriticalGraph.end() )
   {
      return;
   }

   CriticalGraph[From].SuccJobSet.insert(To);
}

void __fastcall HCritical::CalcPath()
{
   int StackP = 0;
   vector<UTF8String> JobStack;
   set<UTF8String> JobSet;

   JobStack.push_back("START");
   JobSet.insert("START");

   for ( ; ; )
   {
      HCriticalNode &ThisNode = CriticalGraph[JobStack[StackP]];
      for ( ; ThisNode.SuccJobIdx < ThisNode.SuccJobVec.size(); )
      {
         UTF8String &SuccNodeId = ThisNode.SuccJobVec[ThisNode.SuccJobIdx];
         if ( JobSet.find(SuccNodeId) != JobSet.end() )
         {
            ThisNode.SuccJobIdx++;
            continue;
         }
         HCriticalNode &SuccNode = CriticalGraph[SuccNodeId];
         if ( SuccNode.CompletedFlag )
         {
            if ( SuccNode.MaxTime >= ThisNode.MaxTime )
            {
               ThisNode.MaxTime = SuccNode.MaxTime;
               ThisNode.MaxNode = SuccNodeId;
            }
            ThisNode.SuccJobIdx++;
         }
         else
         {
            StackP++;
            if ( StackP == JobStack.size() )
            {
               JobStack.push_back(SuccNodeId);
            }
            else
            {
               JobStack[StackP] = SuccNodeId;
            }
            JobSet.insert(SuccNodeId);
            break;
         }
      }
      if ( ThisNode.SuccJobIdx == ThisNode.SuccJobVec.size() )
      {
         ThisNode.CompletedFlag = true;
         ThisNode.MaxTime += ThisNode.Time;
         if ( StackP == 0 )
         {
            break;
         }
         JobSet.erase(JobStack[StackP]);
         StackP--;
      }
   }
}

void __fastcall HCritical::Calculate(vector<HJobListInfo*> &Result,
   set<UTF8String> &ExcludeSet)
{
   InitGraph(ExcludeSet);
   CalcPath();
   Result.clear();

   for ( UTF8String Node = "START"; Node != ""; )
   {
      HCriticalNode &Job = CriticalGraph[Node];
      if ( Node != "START" && !Job.ExcludeFlag )
      {
         Result.push_back(&Graph[Job.Name]);
      }
      Node = Job.MaxNode;
   }
}

#pragma package(smart_init)
